package com.example.leonard.common.utils;

import com.alibaba.fastjson.JSON;
import com.example.leonard.common.enums.FontStyleEnum;
import com.example.leonard.service.impl.EmployeeServiceImpl;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @auther heyang
 * @date 2021/3/29 21:12
 */
public class ExcelUtils {
    /**
     * 表头字体大小
     */
    private static String headerFontSize = "13";
    /**
     * 表头字体样式
     */
    private static String headerFontName = FontStyleEnum.MicrosoftYahei.name();
    /**
     * 数据字体大小
     */
    private static String fontSize = "10";
    /**
     * 数据字体样式
     */
    private static String fontName = FontStyleEnum.TimesNewRoman.name();
    /**
     * 单元格宽度
     */
    private static Integer width = 30;
    /**
     * sheet的名字
     */
    private static String sheetName = "sheetName";
    /**
     * 每个sheet存放的数据量
     */
    private static Integer sheetLength = 1000000;
    /**
     * 是否开启表头样式,默认为true,开启
     */
    private static Boolean isOpeanHeaderStyle = true;
    private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeServiceImpl.class);
    private List<String> headerList = new ArrayList<>();


    /**
     *
     */
    String fileName = "employee";
    /**
     * ##############是否开始其他数据样式,默认为false,关闭(不建议开启,数据量大时影响性能)################
     */
    private static Boolean isOpeanOtherStyle = false;
    // 工作薄
    private SXSSFWorkbook wb = new SXSSFWorkbook(100);
    private SXSSFSheet sheet;
    // 导出数据的必要条件
    private final Class<?> aclazz;
    private final HashMap<Object, String> headerMap;

    public ExcelUtils(Class<?> aclazz, HashMap<Object, String> headerMap) {
        this.aclazz = aclazz;
        this.headerMap = headerMap;
        this.sheet = this.CreatSheet();
    }

    private SXSSFSheet CreatSheet() {
        //创建一个Sheet
        sheet = wb.createSheet(sheetName);
        // SHEET
        sheet.setDefaultColumnWidth(20);
        sheet.setDefaultRowHeightInPoints(16);
   /*     // FONT
        Font font = wb.createFont();
        font.setBold(true)*/
        ;
        // 设置表格样式
        CellStyle cellStyle = setCellStyle();

        // 产生表格标题行
        Cell cell = null;
        Row row = sheet.createRow(0);
        Field[] fields = aclazz.getDeclaredFields();
        int i = 0;
        LOGGER.info("map的信息->", JSON.toJSONString(headerMap));
        for (int indexNum = 0; indexNum < fields.length; indexNum++) {
            if (headerMap.get(fields[indexNum].getName()) == null) {
                continue;
            }
            cell = row.createCell(i);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(headerMap.get(fields[indexNum].getName()));
            LOGGER.info("map的key->{}", headerMap.get(fields[indexNum].getName()));
            headerList.add(fields[indexNum].getName());
            i++;
        }
        return sheet;
    }

    private SXSSFSheet creatSheet(String sheetName) {
        //创建一个Sheet
        sheet = wb.createSheet(sheetName);
        // SHEET
        sheet.setDefaultColumnWidth(20);
        sheet.setDefaultRowHeightInPoints(16);
   /*     // FONT
        Font font = wb.createFont();
        font.setBold(true)*/
        ;
        // 设置表格样式
        CellStyle cellStyle = setCellStyle();

        // 产生表格标题行
        Cell cell = null;
        Row row = sheet.createRow(0);
        Field[] fields = aclazz.getDeclaredFields();
        int i = 0;
        LOGGER.info("map的信息->", JSON.toJSONString(headerMap));
        for (int indexNum = 0; indexNum < fields.length; indexNum++) {
            if (headerMap.get(fields[indexNum].getName()) == null) {
                continue;
            }
            cell = row.createCell(i);
            cell.setCellStyle(cellStyle);
            cell.setCellValue(headerMap.get(fields[indexNum].getName()));
            LOGGER.info("map的key->{}", headerMap.get(fields[indexNum].getName()));
            headerList.add(fields[indexNum].getName());
            i++;
        }
        return sheet;
    }


    /**
     * 创建一个header
     * 创建一个sheet
     * 然后创建cell
     *
     * @param list
     */
    public void exportExcel(List<?> list, HttpServletResponse response) {
        long startTime = System.currentTimeMillis();
        // 传入数据的大小
        int listSize = list.size();
        // 用于计数,每100w时重新开始创建行
        int temp = 0;
        // 用于创建不同的sheetName
        int sheetNameEnd = 0;
        // 这个二重循环不知道有没有优化的空间了
        //获取全部的字段
        try {
            for (int i = 0; i < listSize; i++, temp++) {
                // 每100w重新创建一个新的sheet
                if (i % sheetLength == 0 && i != 0) {
                    sheetNameEnd++;
                    // 创建新的sheet
                    sheet = this.creatSheet(sheetName + sheetNameEnd);
                    // 新的sheet设置新的单元格宽度
                    // setHeaderStyle(o, sh);
                    temp = 0;
                }
                SXSSFRow row = sheet.createRow(i + 1);
                Object o1 = list.get(i);
                Class<?> o1aClass = o1.getClass();
                Field[] fields = o1aClass.getDeclaredFields();
                int indexNum = 0;
                for (Object o : headerList) {
                    SXSSFCell cell = row.createCell(indexNum);
                    for (int i1 = 0; i1 < fields.length; i1++) {
                        Field field = fields[i1];
                        field.setAccessible(true);
                        boolean b = field.getName().equals(o.toString());
                        System.out.println(b);
                        if (b) {
                            //获取属性的值
                            Object o2 = field.get(o1);
                            LOGGER.info("map的key->{}", "", o.toString());
                            System.out.println(o2.toString());
                            String cellValue = "";
                            if (o == null) {
                                cellValue = "";
                            } else if (o2 instanceof Date) {
                                cellValue = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(o2);
                            } else if (o2 instanceof Float || o2 instanceof Double) {
                                cellValue = new BigDecimal(o2.toString()).setScale(2, BigDecimal.ROUND_HALF_UP).toString();
                            } else {
                                cellValue = o2.toString();
                            }
                            cell.setCellValue(cellValue);

                        }
                    }
                    indexNum++;
                }
            }

            //设置
            //response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename="
                    + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
            // 这个操作也非常的耗时,暂时不知道和什么有关,应该该和文件的大小有关
            OutputStream outputStream = response.getOutputStream();
            wb.write(outputStream);
            // 刷新缓冲区
            outputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 不要对HttpServletResponse对象的ServletOutputStream做流关闭处理,否则会报UT010029: Stream is closed异常
            // outputStream.close();
            // 释放内存空间
            wb.dispose();
        }
        long stopTime = System.currentTimeMillis();
        LOGGER.info("导出到excel时间共计: " + (stopTime - startTime) / 1000 + "s");
    }

    private CellStyle setCellStyle() {
        // 创建自定义样式类
        CellStyle style = wb.createCellStyle();
        // 创建自定义字体类
        Font font = wb.createFont();
        // 设置字体样式
        font.setFontName(fontName);
        // 设置字体大小
        font.setFontHeightInPoints(Short.parseShort(fontSize));
        // 设置对齐方式
        style.setAlignment(HorizontalAlignment.CENTER);
        // 设置边框
        style.setBorderBottom(BorderStyle.MEDIUM);
        style.setBorderLeft(BorderStyle.MEDIUM);
        style.setBorderRight(BorderStyle.MEDIUM);
        style.setBorderTop(BorderStyle.MEDIUM);
        // 表头字体加粗
        font.setBold(true);
        style.setFont(font);
        return style;
    }


}
